package com.terry.trace.plugin;

import com.terry.trace.core.LogTagTool;
import com.terry.trace.core.TraceLogger;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.cloud.stream.annotation.StreamListener;
import org.springframework.core.annotation.AnnotatedElementUtils;
import org.springframework.core.annotation.Order;

import java.lang.reflect.Method;

/**
 * spring cloud stream trace配置类
 * PS： @Order的值比在@LogAround小，使得设置flowNo以及清除logTag的操作能包围@LogAround的操作
 * @author TerryJiang
 */
@Order(-5)
@Aspect
public class SpringCloudStreamTraceConfig {

    private static final TraceLogger LOGGER = TraceLogger.getLogger(SpringCloudStreamTraceConfig.class);

    /**
     * 切入点
     */
    @Pointcut("@annotation(org.springframework.cloud.stream.annotation.StreamListener)")
    public void streamListenerAround() {
    }

    /**
     * 方法环绕
     * @param joinPoint
     * @return
     * @throws Throwable
     */
    @Around("streamListenerAround()")
    public Object around(ProceedingJoinPoint joinPoint) throws Throwable {
        Object result;
        try {
            result = joinPoint.proceed();
        }  finally {
            removeContextLogTag(joinPoint);
        }
        return result;
    }

    /**
     * 清空上下文中的log tag
     * @param joinPoint
     */
    private void removeContextLogTag(ProceedingJoinPoint joinPoint) {
        try {
            MethodSignature signature = (MethodSignature) joinPoint.getSignature();
            Method method = signature.getMethod();

            StreamListener streamListenerAnnotation = AnnotatedElementUtils
                    .findMergedAnnotation(method, StreamListener.class);
            // 注解不为空才需要清空
            if(streamListenerAnnotation != null) {
                // 要放在最后一步执行，关键步骤，清空上下文中的log tag, 用来防止内存泄露，或者产生脏数据(如果未做清理，线程复用时，flowNo等信息会用的是旧的)
                LogTagTool.removeContextLogTag();
            }
        } catch (Exception e) {
            LOGGER.warn(SpringCloudStreamTraceConfig.class.getSimpleName() + ".removeContextLogTag error. exception：{}", e);
        }
    }
}
